package com.essence.grabiec104.grab.strategy;

import com.essence.common.model.Substation;
import com.essence.grabiec104.grab.dao.AlarmDao;
import com.essence.grabiec104.grab.model.AlarmRecord;
import com.essence.grabiec104.grab.model.AlarmRule;
import com.essence.grabiec104.grab.model.PointInfo;
import com.essence.grabiec104.grab.model.query.PointInfoQuery;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Component("ycSlopeStrategy")
@Slf4j
public class YcSlopeStrategy extends AbstractSocketAlarmStrategy {
    @Autowired
    private AlarmDao alarmDao;

    public static final int ALARM_SCAN_FLAG_BIT = 4;

    private final Map<Long, List<PointInfoQuery>> ycPoints = new HashMap<>();

    private long sinceTimestamp = System.currentTimeMillis();

    @Override
    protected boolean doHandle(List<AlarmRule> rules) {
        // 获取需要扫描的站点列表
        List<Substation> substations = alarmDao.getAllSubstation().stream()
                .filter(sub -> sub.isRtu() && (sub.getScanFlag() >> ALARM_SCAN_FLAG_BIT & 1L) == 0L)
                .collect(Collectors.toList());
        if (substations.isEmpty()) {
            return false;
        }

        // 过滤出需要扫描的规则
        Map<Long, List<AlarmRule>> scanRules = rules.stream()
                .filter(rule -> rule.getMetricTypeIndex() == AlarmRule.MetricTypeEnum.YC_SLOPE.ordinal())
                .filter(rule -> substations.stream()
                        .map(Substation::getId)
                        .anyMatch(id -> id == rule.getSubstation().getId())
                )
                // 按照 substation.id 分组
                .collect(Collectors.groupingBy(
                        rule -> rule.getSubstation().getId()
                ));
        if (scanRules.isEmpty()) {
            return false;
        }

        for (Long substationId : scanRules.keySet()) {
            List<PointInfoQuery> incPointInfos = alarmDao.selectPointInfoSinceTime(substationId, sinceTimestamp,"Upstream", 1);
            sinceTimestamp = System.currentTimeMillis();
            List<AlarmRule> rs = scanRules.get(substationId);
            List<Long> ruleIds = rs.stream().map(AlarmRule::getId).collect(Collectors.toList());
            for (PointInfoQuery pi : incPointInfos) {
                pi.getRuleIds().addAll(ruleIds);
            }
            List<PointInfoQuery> innerPointInfos = ycPoints.getOrDefault(substationId, new ArrayList<>());
            innerPointInfos.addAll(incPointInfos);
            ycPoints.put(substationId, innerPointInfos);

            for (PointInfoQuery pi : innerPointInfos) {
                ruleValidation(pi, rs);
            }

            innerPointInfos.removeIf(pointInfoQuery -> pointInfoQuery.getRuleIds().isEmpty());
            Substation sub = alarmDao.getSubstationById(substationId);
            long scanFlag = sub.getScanFlag() | (1L << ALARM_SCAN_FLAG_BIT);
            alarmDao.updateSubstationScanFlag(substationId, scanFlag);
        }
        return true;
    }

    private void ruleValidation(PointInfoQuery pi, List<AlarmRule> rules) {
        for (AlarmRule rule : rules) {
            if (System.currentTimeMillis() - pi.getTimestamp() < rule.getIntervalSeconds() * 1000L) {
                continue;
            }
            if (!pi.getRuleIds().contains(rule.getId())) {
                continue;
            }
            pi.getRuleIds().remove(rule.getId());
            int pointAddress = pi.getPointAddress();
            long startTime = pi.getTimestamp();
            long endTime = pi.getTimestamp() + rule.getIntervalSeconds() * 1000L;
            PointInfo lastPoint = alarmDao.selectLastPointInfoByAddressAndTimeRange(pointAddress, startTime, endTime);
            if (lastPoint != null) {
                double slope = Math.abs(lastPoint.getPointValue() - pi.getPointValue()) / pi.getPointValue();
                if (slope > rule.getThreshold() / 100) {
                    if (endTime <= System.currentTimeMillis()) {
                        AlarmRecord record = new AlarmRecord();
                        record.setAlarmRuleId(rule.getId());
                        record.setStateId(rule.getSubstation().getId());
                        record.setPacketId(pi.getPacketId());
                        record.setPointId(pi.getPointId());
                        record.setPointAddress(pi.getPointAddress());
                        record.setMaxThreshold(slope);
                        record.setStatus(0);
                        record.setCreatedAt(System.currentTimeMillis());
                        record.setUpdatedAt(System.currentTimeMillis());
                        record.setSubstation(rule.getSubstation());
                        record.setAlarmRule(rule);
                        alarmDao.insertAlarm(record);
                        publish("append-alarm-record", List.of(record));
                        log.info("告警类型: YC_SLOPE(遥测值跳变) 新增告警记录");
                    }
                }
            }
        }
    }
}
